/*--------------------------------------------------------------------------
Example.c
****************************************
**  Copyright  (C)    2021-2022   **
**  Web:              http://rothd.cn   **
****************************************
--------------------------------------------------------------------------*/

#include "MC3172.h"
#include <math.h>
#include "common.h"
#include "test.h"
////////////////////////////////////////////////////////////

// LED测试
void LED0_GPIOA_PIN0_TEST(void)
{
    // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN0引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN0, GPIO_SET_ENABLE);

    while (1)
    {
        // GPIOA PIN0输出1
        GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN0);

        // 延时
        // for (u32 var = 0; var < THREAD_CLOCK_4/100; ++var)
        // {
        //     NOP();
        // }
        delay_us_nop(100);

        // GPIOA PIN0输出0
        GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN0);

        // 延时
        // for (u32 var = 0; var < THREAD_CLOCK_4/100; ++var)
        // {
        //     NOP();
        // }
        delay_us_nop(100);
    }
}

void LED1_GPIOA_PIN1_TEST(void)
{
    // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN1引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN1, GPIO_SET_ENABLE);

    while (1)
    {
        // GPIOA PIN1输出1
        GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN1);

        // 延时
        // for (u32 var = 0; var < CORE_CLOCK_HZ_4/3; ++var)
        // {
        //     NOP();
        // }
        delay_us(100);
        // delay(1);

        // GPIOA PIN1输出0
        GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN1);

        // 延时
        // for (u32 var = 0; var < CORE_CLOCK_HZ_4/3; ++var)
        // {
        //     NOP();
        // }
        delay_us(100);
        // delay(1);
    }
}

void LED2_GPIOA_PIN2_TEST(void)
{
    // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN1引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN2, GPIO_SET_ENABLE);

    while (1)
    {
        // GPIOA PIN1输出1
        GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN2);
        delay_us_nop(100000);

        // GPIOA PIN1输出0
        GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN2);
        delay_us_nop(500000);
    }
}

void LED6_GPIOA_PIN6_TEST(void)
{
    // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN1引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN6, GPIO_SET_ENABLE);

    while (1)
    {
        // GPIOA PIN1输出1
        GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN6);
        delay_us_nop(500000);

        // GPIOA PIN1输出0
        GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN6);
        delay_us_nop(100000);
    }
}

// SPI测试
void GPCOM_SPI_WS2812B(u32 gpcom_sel)
{
    volatile u8 rx_data_temp[8];

    // 初始化控制禁用CS和MISO使能，只使用MOSI作为WS2812的输出引脚。
    /*
        P0: CS
        P1: CLK         [*]
        P2: MOSI        [*]
        P3: MISO
    */

    // 启动GPIOA，并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(gpcom_sel, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    // 设置 MASTER_IN 输入管脚：P3
    GPCOM_SET_IN_PORT(gpcom_sel, (GPCOM_MASTER_IN_IS_P3));

    // 设置 CS 、MASTER_CLK 、 MASTER_OUT 管脚：P0 P1 P2
    GPCOM_SET_OUT_PORT(gpcom_sel, (
                                      GPCOM_P0_OUTPUT_DISABLE | GPCOM_P1_OUTPUT_DISABLE | GPCOM_P2_OUTPUT_ENABLE | GPCOM_P3_OUTPUT_DISABLE |
                                      GPCOM_P0_IS_HIGH | GPCOM_P1_IS_MASTER_CLK | GPCOM_P2_IS_MASTER_OUT | GPCOM_P3_IS_HIGH));

    // 设置通信接口的工作模式为 SPI_MASTER 的 MODE3 模式
    GPCOM_SET_COM_MODE(gpcom_sel, (GPCOM_SPI_MASTER_MODE3 | GPCOM_TX_MSB_FIRST | GPCOM_RX_MSB_FIRST));

    // 设置 SPI 的工作速度，24000000 是该外设的实际时钟频率，1000000 是 SPI 速率
    //GPCOM_SET_COM_SPEED(gpcom_sel, CORE_CLOCK_HZ_4, (int)(8 / 1.25e-6));
    GPCOM_SET_COM_SPEED(gpcom_sel, CORE_CLOCK_HZ, 1000);

    // 设置 SPI 管脚映射 GPIO
    GPCOM_SET_OVERRIDE_GPIO(gpcom_sel, (GPCOM_P1_OVERRIDE_GPIO | GPCOM_P2_OVERRIDE_GPIO));

    // 缓存指针
    u8 tx_data_wp = 0;
    u8 rx_data_rp = 0;
    u16 nops = 0;

    // 读取当前 TX 的缓存 FIFO 写指针
    tx_data_wp = GPCOM_GET_TX_WP(gpcom_sel);

    // 读取当前 RX 的缓存 FIFO 写指针
    rx_data_rp = GPCOM_GET_RX_WP(gpcom_sel);

    while (1)
    {
        // 设置 P0_IS_LOW，拉低 CS，也可通过 GPIO 等效设置
        // GPCOM_SET_OUT_PORT(gpcom_sel, (
        //                                   GPCOM_P0_OUTPUT_DISABLE | GPCOM_P1_OUTPUT_ENABLE | GPCOM_P2_OUTPUT_ENABLE | GPCOM_P3_OUTPUT_DISABLE |
        //                                   GPCOM_P0_IS_LOW | GPCOM_P1_IS_MASTER_CLK | GPCOM_P2_IS_MASTER_OUT | GPCOM_P3_IS_HIGH));

        u8 n = 3;
        while (n--)
        {
            // 把数据写入 TX BUFFER 的 0 到 3 号缓存,只是写入，并不启动传输
            GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 0, 0xf8);
            GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 1, 0xf8);
            GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 2, 0xf8);
            GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 3, 0xf8);
            // GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 4, 0x00);            
            // GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 5, 0x00);
            // GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 6, 0x00);
            // GPCOM_SEND_TX_DATA(gpcom_sel, tx_data_wp + 7, 0x00);

            tx_data_wp += 4;
            // 通知外设 TX BUFFER 的缓存已经写入，将外设 GPCOM3_TX_WP 设置为 4，启动传输
            GPCOM_SEND_TX_WP(gpcom_sel, tx_data_wp);

            // 检查 TX 的发射缓存是否已空
            while (!(GPCOM_TX_FIFO_EMPTY(gpcom_sel)))
            {
            };
            // 用于等待 GPCOM3_TX_RP 等于 GPCOM3_TX_WP，这意味着外设已经传输完毕所有待发送数据
            // while(GPCOM3_TX_RP!=GPCOM3_TX_WP){};
            // GPCOM3_TX_RP 是外设的读指针，只要外设发现他的 GPCOM3_TX_RP 不等于 GPCOM3_TX_WP，就会发送数据并自加 1，直至追上 GPCOM3_TX_WP。

            // delay_us_nop(12);
            nops=15;
            while (nops--)
            {
                NOP();
            }            
        }

        // delay(1);
        nops=12*50;
        while (nops--)
        {
            NOP();
        }

// 判断 RX 是否有数据，并把数据搬移至数组 rx_data_temp。rx_data_rp 为本地变量，GPCOM3_RX_WP 为外设的 RX FIFO 写指针
#if 0
        while (rx_data_rp != GPCOM_GET_RX_WP(gpcom_sel))
        {
            rx_data_temp[rx_data_rp & 0x7] = GPCOM_GET_RX_DATA(gpcom_sel, rx_data_rp);
            rx_data_rp += 1;
            rx_data_rp &= 0xf;
        };
#endif

        // 通过设置 P0_IS_HIGH，拉高 CS，也可通过 GPIO 等效设置
        // GPCOM_SET_OUT_PORT(gpcom_sel, (
        //                                   GPCOM_P0_OUTPUT_DISABLE | GPCOM_P1_OUTPUT_ENABLE | GPCOM_P2_OUTPUT_ENABLE | GPCOM_P3_OUTPUT_DISABLE |
        //                                   GPCOM_P0_IS_HIGH | GPCOM_P1_IS_MASTER_CLK | GPCOM_P2_IS_MASTER_OUT | GPCOM_P3_IS_HIGH));

        delay(2);
    }
}

#define GPCOM_RX_STATUS_ADDR 0x0000025b
#define GPCOM_GET_RX_STATUS(GPCOM_SEL) (*(volatile u8 *)(GPCOM_SEL + GPCOM_RX_STATUS_ADDR))
void GPCOM_SPI_SLAVE_EXAMPLE(u32 gpcom_sel)
{
    volatile u8 rx_data_temp[8];

    INTDEV_SET_CLK_RST(gpcom_sel, (INTDEV_RUN | INTDEV_IS_GROUP0 | INTDEV_CLK_IS_CORECLK_DIV2));

    GPCOM_SET_IN_PORT(gpcom_sel, (GPCOM_SLAVE_CS_IS_P0 | GPCOM_SLAVE_CLK_IS_P1 | GPCOM_SLAVE_IN_IS_P2));
    GPCOM_SET_OUT_PORT(gpcom_sel, (
                                      GPCOM_P0_OUTPUT_DISABLE | GPCOM_P1_OUTPUT_DISABLE | GPCOM_P2_OUTPUT_DISABLE | GPCOM_P3_OUTPUT_ENABLE |
                                      GPCOM_P0_IS_HIGH | GPCOM_P1_IS_HIGH | GPCOM_P2_IS_HIGH | GPCOM_P3_IS_SLAVE_OUT));

    GPCOM_SET_COM_MODE(gpcom_sel, (GPCOM_SPI_SLAVE_MODE3 | GPCOM_TX_MSB_FIRST | GPCOM_RX_MSB_FIRST));

    GPCOM_SET_COM_SPEED(gpcom_sel, 24000000, 1000000);

    GPCOM_SET_OVERRIDE_GPIO(gpcom_sel, (
                                           GPCOM_P0_OVERRIDE_GPIO | GPCOM_P0_INPUT_ENABLE |
                                           GPCOM_P1_OVERRIDE_GPIO | GPCOM_P1_INPUT_ENABLE |
                                           GPCOM_P2_OVERRIDE_GPIO | GPCOM_P2_INPUT_ENABLE |
                                           GPCOM_P3_OVERRIDE_GPIO));

    u8 tx_data_wp = 0;
    u8 rx_data_rp = 0;

    GPCOM_SEND_TX_DATA(gpcom_sel, 0, CORE_CNT);

    while (1)
    {

        while (rx_data_rp != GPCOM_GET_RX_WP(gpcom_sel))
        {
            rx_data_temp[rx_data_rp & 0x7] = GPCOM_GET_RX_DATA(gpcom_sel, rx_data_rp);
            rx_data_rp += 1;
            rx_data_rp &= 0xf;
        };
    }
}
////////////////////////////////////////////////////////////
